import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

public class FileStats {

	ArrayList<frameInfo> frameInfoList;

	final double shotHistThreshold = 30000;
	final int shotSizeThreshold = 20;
	final int minShotSize = 32; // 1sec = 2*24 frames since files are 24fps

	int prevShotStart = 0;
	int maxFrameID = 0;
	
	double minMV = Double.MAX_VALUE;
	double maxMV = 0;
	double minHist = Double.MAX_VALUE;
	double maxHist = 0;
	
	double minAud = Double.MAX_VALUE;
	double maxAud = Double.MIN_VALUE;

	FileStats() {
		frameInfoList = new ArrayList<frameInfo>();
	}

	void getInterestingSequences(double fraction,
			ArrayList<VideoRange> interesting) {
		
		/*
		* Assume histogram differences are generally low, with some far outliers.
		* Outliers are scene changes 
		*/
		
//		//calc mean
//		double meanHist = 0;
//		for(int i = 0; i < frameInfoList.size(); i++)
//			meanHist += frameInfoList.get(i).histogramDifference;
//		meanHist /= frameInfoList.size();
//		
//		//calc SD
//		double sdHist = 0;
//		for(int i = 0; i < frameInfoList.size(); i++)
//			sdHist += (frameInfoList.get(i).histogramDifference - meanHist) * (frameInfoList.get(i).histogramDifference - meanHist);
//		sdHist /= frameInfoList.size();
//		sdHist = Math.sqrt(sdHist);
		
		// Normalize values and compute scores
//		for (int i = 0; i < frameInfoList.size(); i++) {
//			frameInfo thisFrame = frameInfoList.get(i);
//
//			double normHist = (thisFrame.histogramDifference - minHist)
//					/ (maxHist - minHist);
//			double normMV = (thisFrame.motion - minMV) / (maxMV - minMV);
//			
//			double normAud = (thisFrame.audio - minAud) / (maxAud - minAud);
//			
//			thisFrame.score = 0.4 * normAud + 0.4 * normHist + 0.4 * normMV;
//			
//		}
		
		//Scan through looking for shot changes, and find each shot's score
//		System.out.println("Mean: " + meanHist + " SD : " + sdHist);
//		VideoRange range = new VideoRange();
//		range.startFrameID = 0;
//		double rangeScore = frameInfoList.get(0).score;
//		for(int i = 1; i < frameInfoList.size() - 1; i++) {
//			frameInfo thisFrame = frameInfoList.get(i);
//			
//			if(Math.abs(thisFrame.histogramDifference) > (meanHist + 3*sdHist)) {
//				System.out.println("Shot change detected at " + i + "," + thisFrame.histogramDifference);
//				
//				range.stopFrameID = i - 1;
//				range.score = rangeScore;
//				interesting.add(range);
//				System.out.println("Inserting range " + range.startFrameID + "->" + range.stopFrameID + "," + rangeScore);
//				
//				rangeScore = frameInfoList.get(i).score;
//				range = new VideoRange();
//				range.startFrameID = i;
//				i += minShotSize/2;
//			}
//			rangeScore += thisFrame.score;
//		}
//		
//		range.stopFrameID = frameInfoList.size() - 1;
//		interesting.add(range);
//		
//		// Get top scoring shots
//		Collections.sort(interesting, new RangeScoreComparer());
//		
//		System.out.println("Detected " + interesting.size() + " shots");
//		
//		//Keep only top fraction of them, delete the rest
//		int size = interesting.size();
//		// int start = (int) (fraction * size) / (minShotSize / 2);
//		int start = (int) (fraction * size);
//		for (int i = size - 1; i > start; i--)
//			interesting.remove(i);
//		
//		//Return to chronological order
//		Collections.sort(interesting, new RangeComparer());
//		
		int totalFrames = 0;

		// Normalize values and compute scores
		for (int i = 0; i < frameInfoList.size(); i++) {
			frameInfo thisFrame = frameInfoList.get(i);

			double normHist = (thisFrame.histogramDifference - minHist)
					/ (maxHist - minHist);
			double normMV = (thisFrame.motion - minMV) / (maxMV - minMV);
			
			double normAud = (thisFrame.audio - minAud) / (maxAud - minAud);
			
			//System.out.println(normAud);
			thisFrame.score = 0.3 * normAud + 0.2 * normHist + 0.5 * normMV;

		}

		 System.out.println(totalFrames);
		
		// Get top scoring frames
		Collections.sort(frameInfoList, new FrameScoreComparer());
//		System.out.println("----");
//
//		System.out.println((int) (fraction * frameInfoList.size()));
		int size = frameInfoList.size();
		//int start = (int) (fraction * size) / (minShotSize / 2);
		int start = (int) (fraction * size);
		for (int i = size - 1; i > start; i--)
			frameInfoList.remove(i);

		// for (int i = 0; i < frameInfoList.size(); i++) {
		// System.out.print(frameInfoList.get(i).frameID + ", ");
		// if(i%10 == 0)
		// System.out.println("");
		// }
		// System.out.println("\n----");

		//Collections.sort(frameInfoList, new FrameComparer());
		
		for(int i = 0; i < frameInfoList.size(); i++) {
			
			if(totalFrames > fraction * maxFrameID) {
				//Finished creating list
				break;
			}
			frameInfo thisFrame = frameInfoList.get(i);

			VideoRange newRange = new VideoRange();
			newRange.startFrameID = thisFrame.frameID - (int) (minShotSize / 2);
			if (newRange.startFrameID < 0)
				newRange.startFrameID = 0;
			newRange.stopFrameID = newRange.startFrameID + minShotSize - 1;
			
			//System.out.println("Insert new range " + newRange.startFrameID + "," + newRange.stopFrameID);
			
			boolean done = false;
			for(int j = 0; j < interesting.size(); j++) {
				VideoRange currRange = interesting.get(j);
				//System.out.println("CurrRange: " + currRange.startFrameID + "," + currRange.stopFrameID);
				if(newRange.startFrameID < currRange.startFrameID && newRange.stopFrameID < currRange.startFrameID) {
					//The new range is completely to the left of the current range, insert it here");
					interesting.add(j, newRange);
					totalFrames += newRange.stopFrameID - newRange.startFrameID;
					done = true;
					break;
				} else if(newRange.startFrameID > currRange.stopFrameID && newRange.stopFrameID > currRange.stopFrameID) {
					//The new range is completely to the right of the current, keep going"); 
				} else if(newRange.startFrameID <= currRange.startFrameID && newRange.stopFrameID >= currRange.stopFrameID) {
					//new range is complete superset of current, expand current");
					totalFrames += newRange.stopFrameID - currRange.stopFrameID;
					totalFrames += currRange.startFrameID - newRange.startFrameID;
					currRange.stopFrameID = newRange.stopFrameID;
					currRange.startFrameID = newRange.startFrameID;
					done = true;
					break;
				} else if(newRange.startFrameID >= currRange.startFrameID && newRange.stopFrameID <= currRange.stopFrameID) {
					//new range is complete subset of current, do nothing");
					done = true;
					break;
				} else if(newRange.startFrameID < currRange.startFrameID && newRange.stopFrameID <= currRange.stopFrameID) {
					//new range is left overlap of current, expand current");
					totalFrames += currRange.startFrameID - newRange.startFrameID;
					currRange.startFrameID = newRange.startFrameID;
					done = true;
					break;
				} else if(newRange.startFrameID >= currRange.startFrameID && newRange.stopFrameID > currRange.stopFrameID) {
					//New range is right overlap of current range, expand the current range");
					totalFrames += newRange.stopFrameID - currRange.stopFrameID;
					currRange.stopFrameID = newRange.stopFrameID;
					done = true;
					break;
					
				} else {
					//Impossible! Panic!");
				}
			}
			if(!done) {
				//insert range at the end");
				interesting.add(newRange);
				totalFrames += newRange.stopFrameID - newRange.startFrameID;
				done = true;
			}
		}
	}

	void insertData(int frameID, double motion, double histoDiff) {

		frameInfoList.add(new frameInfo(frameID, motion, histoDiff));

		if (frameID > maxFrameID)
			maxFrameID = frameID;

		if (motion > maxMV)
			maxMV = motion;

		if (motion < minMV)
			minMV = motion;

		if (histoDiff > maxHist)
			maxHist = histoDiff;

		if (histoDiff < minHist)
			minHist = histoDiff;
	}

	public class RangeScoreComparer implements Comparator<VideoRange> {

		@Override
		public int compare(VideoRange arg0, VideoRange arg1) {
			return (arg0.score > arg1.score ? -1
					: (arg0.score == arg1.score ? 0 : 1));
		}

	}	


	public class RangeComparer implements Comparator<VideoRange> {

		@Override
		public int compare(VideoRange arg0, VideoRange arg1) {
			return (arg0.startFrameID < arg1.startFrameID ? -1
					: (arg0.startFrameID == arg1.startFrameID ? 0 : 1));
		}

	}	
	
	public class FrameScoreComparer implements Comparator<frameInfo> {

		@Override
		public int compare(frameInfo arg0, frameInfo arg1) {
			return (arg0.score > arg1.score ? -1
					: (arg0.score == arg1.score ? 0 : 1));
		}

	}

	public class FrameComparer implements Comparator<frameInfo> {

		@Override
		public int compare(frameInfo arg0, frameInfo arg1) {
			return (arg0.frameID < arg1.frameID ? -1
					: (arg0.frameID == arg1.frameID ? 0 : 1));
		}

	}

}
